home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
NOVA - For the NeXT Workstation
/
NOVA - For the NeXT Workstation.iso
/
SourceCode
/
AdobeExamples
/
NX_ImportAdv
/
Document.m
< prev
next >
Wrap
Text File
|
1992-12-19
|
20KB
|
811 lines
/*
* (a) (C) 1990 by Adobe Systems Incorporated. All rights reserved.
*
* (b) If this Sample Code is distributed as part of the Display PostScript
* System Software Development Kit from Adobe Systems Incorporated,
* then this copy is designated as Development Software and its use is
* subject to the terms of the License Agreement attached to such Kit.
*
* (c) If this Sample Code is distributed independently, then the following
* terms apply:
*
* (d) This file may be freely copied and redistributed as long as:
* 1) Parts (a), (d), (e) and (f) continue to be included in the file,
* 2) If the file has been modified in any way, a notice of such
* modification is conspicuously indicated.
*
* (e) PostScript, Display PostScript, and Adobe are registered trademarks of
* Adobe Systems Incorporated.
*
* (f) THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO
* CHANGE WITHOUT NOTICE, AND SHOULD NOT BE CONSTRUED
* AS A COMMITMENT BY ADOBE SYSTEMS INCORPORATED.
* ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY
* OR LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO
* WARRANTY OF ANY KIND (EXPRESS, IMPLIED OR STATUTORY)
* WITH RESPECT TO THIS INFORMATION, AND EXPRESSLY
* DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT
* OF THIRD PARTY RIGHTS.
*/
/*
* Document.m
*
* Portions of the source code in this file are based on source code
* from the Draw example application provided by NeXT.
*
* The Document class serves to keep track of the global information
* concerning a particular file. It sets up the window and the views
* (ScrollView, DocView and DrawingView) . It manages the name
* of the file as well as save and print info information.
*
* The listenerId is used to allow the user to drag an icon representing
* a PostScript file into the document. The iconPathList is the
* list of files which was last dragged into the document.
*
* The saved variable marks whether a disk file has been
* created for the document (i.e. if it has ever been saved).
*
* Version: 2.0
* Author: Ken Fromm
*/
#import "Document.h"
#import "ImportApp.h"
#import "DocView.h"
#import "DrawingView.h"
#import "ScrollingView.h"
#import "ImportPanel.h"
#import "SaveAsPanel.h"
#import <appkit/Cursor.h>
#import <appkit/Listener.h>
#import <appkit/Matrix.h>
#import <appkit/PageLayout.h>
#import <appkit/PrintInfo.h>
#import <appkit/ScrollView.h>
#import <appkit/Speaker.h>
#import <appkit/nextstd.h>
#import <appkit/publicWraps.h>
#import <objc/hashtable.h>
#import <string.h>
const NXRect DefaultContentRect = {0.0, 0.0, 575.0, 660.0};
static const char DefaultName[] = "Empty Window";
extern void resizeBuffer();
@implementation Document
/* Factory methods */
/*
* Creates a new, empty, document.
*
* Creates a PrintInfo object; creates a view whose size depends on the
* default PrintInfo created; creates a window for that view; sets self
* as the window's delegate; orders the window front; registers the window
* with the Workspace Manager. The default margins are set to 1/4 inch.
*/
+ new
{
self = [super new];
printinfoId = [PrintInfo new];
[printinfoId setMarginLeft:18.0 right:18.0 top:18.0 bottom:18.0];
[self createWindow];
[self setName:NULL andDirectory:NULL];
[self setDocument];
return self;
}
/* Opens an existing document from the specified file. */
+ newFromFile:(const char *)file
{
NXStream *stream;
self = [super new];
stream = NXMapFile(file, NX_READONLY);
[self createWindow];
if ([self readFromStream:stream])
{
[self setName:file];
[self setDocument];
}
else
{
[self free];
self = nil;
}
return self;
}
/* Very private instance method needed by factory methods */
/*
* This method loads an archived document from the
* specified file name. The frame size is read first and then
* the window is displayed before the rest of the document
* is loaded (the print info and the drawing view).
*
* Does not place newView as the window's drawing view until
* the read is complete. If a failure occurs for some reason
* during the read, the previous drawing view is retained.
*
* An NX_DURING handler is needed around the NXTypedStream
* operations because if the user has asked that a bogus file be
* opened, the NXTypedStream will raise an error. To handle the
* error, the NXTypedStream must be closed.
*/
- readFromStream:(NXStream *)stream
{
BOOL err = YES;
NXTypedStream *volatile ts = NULL;
if (stream)
{
NX_DURING
ts = NXOpenTypedStream(stream, NX_READONLY);
if (ts)
{
printinfoId = NXReadObject(ts);
drawingviewId = NXReadObject(ts);
NXCloseTypedStream(ts);
err = NO;
}
NX_HANDLER
NXCloseTypedStream(ts);
NX_ENDHANDLER
NXClose(stream);
}
if (!err)
{
[[docviewId setDrawView:drawingviewId] free];
[docviewId placeView:drawingviewId];
[[windowId contentView] display];
}
else
Notify("Open Error", "Cannot open file.");
return self;
}
- setDocument
{
NXPoint location;
NXRect winFrame;
[docviewId placeView:drawingviewId];
[self registerWindow];
[windowId setDelegate:self];
[windowId display];
[windowId getFrame:&winFrame];
[NXApp getPosition:&location forSize:&winFrame.size];
[windowId moveTo:location.x :location.y];
[windowId makeKeyAndOrderFront:self];
return self;
}
- free
{
[printinfoId free];
[windowId free];
NX_FREE(name);
NX_FREE(directory);
NX_FREE(iconPathList);
return [super free];
}
/*
* Create the drawing window and place a scrollview as the content view.
* A DrawingView instance is placed as the DocView of the ScrollView.
*/
- createWindow
{
id scrollView;
const NXRect *paperRect;
windowId = [Window newContent:&DefaultContentRect
style:NX_TITLEDSTYLE
backing:NX_BUFFERED
buttonMask:NX_ALLBUTTONS
defer:NO];
[windowId addToEventMask:NX_FLAGSCHANGEDMASK];
scrollView = [ScrollingView newFrame:&DefaultContentRect];
[scrollView setBorderType:SCROLLVIEW_BORDER];
paperRect = [printinfoId paperRect];
drawingviewId = [DrawingView newFrame:paperRect];
docviewId = [[[[DocView new] setClipping:NO] setScale:1.0] setFlipped:NO];
[docviewId notifyAncestorWhenFrameChanged:YES];
[docviewId setDrawView:drawingviewId];
[scrollView setDocView:docviewId];
[[docviewId superview] allocateGState];
[[windowId setContentView:scrollView] free];
[windowId makeFirstResponder:drawingviewId];
return self;
}
- window
{
return windowId;
}
/* Returns the DrawingView associated with this document. */
- drawingView
{
return drawingviewId;
}
- docView
{
return docviewId;
}
- printInfo
{
return printinfoId;
}
/* Target/Action methods */
/*
* Puts up a PageLayout panel and allows the user to pick a different
* size paper to work on. The view is then resized to the
* new paper size. The new DrawingView is placed in the DocView
* and then scrolled to the previous rectangle.
* The view is dirtied because the PrintInfo is part of the document.
*/
- changeLayout:sender
{
NXRect visibleRect;
const NXRect *paperRect;
if ([[PageLayout new] runModal] == NX_OKTAG)
{
[drawingviewId getVisibleRect:&visibleRect];
paperRect = [printinfoId paperRect];
[drawingviewId sizeTo:paperRect->size.width :paperRect->size.height];
[docviewId placeView:drawingviewId];
[drawingviewId scrollRectToVisible:&visibleRect];
[[windowId contentView] display];
[drawingviewId setDirty:YES];
}
return self;
}
- print:sender
{
return [drawingviewId printPSCode:sender];
}
/* Revert the document back to what is on the disk. */
- revertToSaved:sender
{
NXStream *stream;
NXRect visibleRect;
const NXRect *paperRect;
if (!saved || ![drawingviewId isDirty] ||
(NXRunAlertPanel("Revert",
"%s has been edited. Are you sure you want to undo changes?",
"Revert", "Cancel", NULL, name) != NX_ALERTDEFAULT))
{
return self;
}
[drawingviewId getVisibleRect:&visibleRect];
[windowId endEditingFor:self];
stream = NXMapFile([self filename], NX_READONLY);
if (stream && [self readFromStream:stream])
{
[windowId disableDisplay];
paperRect = [printinfoId paperRect];
[drawingviewId sizeTo:paperRect->size.width :paperRect->size.height];
[docviewId placeView:drawingviewId];
[drawingviewId scrollRectToVisible:&visibleRect];
[windowId reenableDisplay];
[[windowId contentView] display];
[windowId makeFirstResponder:drawingviewId];
[drawingviewId setDirty:NO];
NXClose(stream);
}
else
{
if (stream)
NXClose(stream);
Notify("Revert Error", "Cannot revert to saved file.");
}
return self;
}
/*
* Bring up the import (open) panel to obtain the file and then pass
* to the drawing view.
*/
- import:sender
{
id importpanel;
static const char *const filetype[4] = {"ps", "eps", "tiff", NULL};
importpanel = [[ImportPanel new] setImport];
if ([importpanel runModalForTypes:filetype])
[drawingviewId importFile:[importpanel filename] at:NULL];
return self;
}
/*
* Writes out the document in archive format.
* (Saves the PrintInfo and DrawingView objects. See
* DrawingView's write: methods for more details.)
*/
- saveFile:(const char *) file
{
BOOL error;
NXTypedStream *typedstream;
error = YES;
typedstream = NXOpenTypedStreamForFile(file, NX_WRITEONLY);
if (typedstream)
{
NXWriteRootObject(typedstream, printinfoId);
NXWriteRootObject(typedstream, drawingviewId);
NXCloseTypedStream(typedstream);
error = NO;
}
else
Notify("Save Error", "Cannot open a typed stream to the file.");
return (error ? nil : self);
}
/*
* Saves the file. If this document has never been saved to disk,
* then a SavePanel is put up to ask the user what file name she
* wishes to use to save the document.
*/
- save:sender
{
id savepanel;
if ([drawingviewId isDirty] || !saved)
{
if (!saved)
{
savepanel = [[SaveAsPanel new] setSave];
if (![savepanel runModalForDirectory:directory file:NULL])
return self;
[self setName:[savepanel filename]];
}
if ([self saveFile:[self filename]])
{
[drawingviewId setDirty:NO];
saved = YES;
}
}
return self;
}
- saveAs:sender
{
id savepanel;
char *tempname;
if (strcmp(name, DefaultName) == 0)
tempname = NULL;
else
tempname = name;
savepanel = [[SaveAsPanel new] setSaveAs];
if ([savepanel runModalForDirectory:directory file:tempname])
{
[drawingviewId setDirty:YES];
[self setName:[savepanel filename]];
if ([self saveFile:[self filename]])
{
[drawingviewId setDirty:NO];
saved = YES;
}
}
return self;
}
/*
* Writes out the document in Epsf/Illustrator format.
*/
- saveTo:sender
{
id savepanel;
NXStream *stream;
savepanel = [[SaveAsPanel new] setSaveTo];
if ([savepanel runModal])
{
stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
if (stream)
{
[drawingviewId writePSToStream:stream];
NXSaveToFile(stream, [savepanel filename]);
NXCloseMemory(stream, NX_FREEBUFFER);
}
else
Notify("Save Error", "Cannot open a stream to the file.");
}
return self;
}
/* Methods related to naming/saving this document. */
/*
* Gets the fully specified file name of the document.
*/
- (const char *)filename
{
static char filenamebuf[MAXPATHLEN+1];
if (!directory || !name)
[self setName:name andDirectory:directory];
if (directory)
{
strcpy(filenamebuf, directory);
strcat(filenamebuf, "/");
}
else
filenamebuf[0] = '\0';
if (name)
strcat(filenamebuf, name);
return filenamebuf;
}
- (const char *)directory
{
return directory;
}
- (const char *)name
{
return name;
}
/*
* If file is a full path name, then both the name and directory of the
* document is updated appropriately, otherwise, only the name is changed.
*/
- setName:(const char *)file
{
char *lastComponent;
char path[MAXPATHLEN+1];
if (file)
{
strcpy(path, file);
lastComponent = strrchr(path, '/');
if (lastComponent)
{
*lastComponent++ = '\0';
return [self setName:lastComponent andDirectory:path];
}
else
return [self setName:file andDirectory:NULL];
}
return self;
}
/*
* Updates the name and directory of the document.
* newName or newDirectory can be NULL, in which case the name or directory
* will not be changed (unless one is currently not set, in which case
* a default name will be used).
*/
- setName:(const char *)newName andDirectory:(const char *)newDirectory
{
if ((newName && *newName) || !name)
{
if (!newName || !*newName)
newName = DefaultName;
NX_FREE(name);
name = NXCopyStringBuffer(newName);
}
if ((newDirectory && (*newDirectory == '/')) || !directory)
{
if (!newDirectory || (*newDirectory != '/'))
newDirectory = [NXApp currentDirectory];
NX_FREE(directory);
directory = NXCopyStringBufferFromZone(newDirectory, [self zone]);
}
[windowId setTitleAsFilename:[self filename]];
return self;
}
/* Window delegate methods. */
/*
* Switch the Application's PrintInfo to the document's when the document
* window becomes the main window. Also set the cursor appropriately
* depending on which tool is currently selected.
*/
- windowDidBecomeMain:sender
{
[NXApp setPrintInfo:printinfoId];
[self resetResponder];
return self;
}
/*
* Prevents the window from getting too large or too small.
*/
- windowWillResize:sender toSize:(NXSize *)size
{
NXSize screenSize;
[NXApp getScreenSize:&screenSize];
screenSize.width = screenSize.width * 0.90;
screenSize.height = screenSize.height * 0.90;
size->width = MIN(screenSize.width, size->width);
size->height = MIN(screenSize.height, size->height);
size->width = MAX(MIN_WINDOW_WIDTH, size->width);
size->height = MAX(MIN_WINDOW_HEIGHT, size->height);
return self;
}
/*
* Resizes the doc view and repositions the drawing view inside the doc view.
*/
- windowDidResize:sender
{
NXRect frameRect, contRect;
[windowId getFrame:&frameRect];
[Window getContentRect:&contRect forFrameRect:&frameRect style:[windowId style]];
resizeBuffer([drawingviewId buffer], &contRect.size);
[docviewId placeView:drawingviewId];
return self;
}
/*
* If the GraphicView has been edited, then this asks the user if she
* wants to save the changes before closing the window. When the window
* is closed, the DrawDocument itself must be freed. This is accomplished
* via Application's delayedFree: mechanism. Unfortunately, by the time
* delayedFree: frees the DrawDocument, the window and view instance variables
* will already have automatically been freed by virtue of the window's being
* closed. Thus, those instance variables must be set to nil to avoid their
* being freed twice.
*
* Returning nil from this method informs the caller that the window should
* NOT be closed. Anything else implies it should be closed.
*/
- windowWillClose:sender
{
int save;
if ([drawingviewId isDirty] && (saved || ![drawingviewId isEmpty]))
{
save = NXRunAlertPanel("Close", "%s has changes. Save them?", "Save",
"Don't Save", "Cancel", name);
if (save == NX_ALERTDEFAULT || save == NX_ALERTALTERNATE)
{
[sender endEditingFor:self]; /* terminate any editing */
if (save == NX_ALERTDEFAULT)
[self save:nil];
}
else
return nil;
}
[self unregisterWindow];
[NXApp setPrintInfo:nil];
return self;
}
/* Icon dragging methods */
/*
* Registers the document window with the Workspace Manager so that when the
* user picks up an icon in the Workspace and drags it over our document window
* and lets go, iconEntered:... and iconReleasedAt::ok: messages will be
* sent to the DrawDocument from the Workspace Manager. Allows the user to
* drag PostScript and TIFF files into the document.
*/
- registerWindow
{
unsigned int windowNum;
id speaker = [NXApp appSpeaker];
listenerId = [Listener new];
[listenerId setDelegate:self];
[listenerId usePrivatePort];
[listenerId addPort];
NXConvertWinNumToGlobal([windowId windowNum], &windowNum);
[speaker setSendPort:NXPortFromName(NX_WORKSPACEREQUEST, NULL)];
[speaker registerWindow:windowNum toPort:[listenerId listenPort]];
return self;
}
/* Undoes what registerWindow does. */
- unregisterWindow
{
unsigned int windowNum;
id speaker = [NXApp appSpeaker];
if (listenerId)
{
[speaker setSendPort:NXPortFromName(NX_WORKSPACEREQUEST, NULL)];
NXConvertWinNumToGlobal([windowId windowNum], &windowNum);
[speaker unregisterWindow:windowNum];
[listenerId free];
}
return self;
}
/*
* Called whenever an icon is dragged from the Workspace over the document
* window. At this point, all that is done is to salt away the list of files
* represented by the icon. All the real work is done in iconReleasedAt::ok:.
*/
- (int)iconEntered:(int)windowNum at:(double)x :(double)y
iconWindow:(int)iconWindowNum iconX:(double)iconX iconY:(double)iconY
iconWidth:(double)iconWidth iconHeight:(double)iconHeight
pathList:(char *)pathList
{
if (!iconPathList || strcmp(iconPathList, pathList))
{
NX_FREE(iconPathList);
NX_MALLOC(iconPathList, char, strlen(pathList)+1);
strcpy(iconPathList, pathList);
}
return 0;
}
/*
* Goes through the list of files associated with the icon dragged
* from the Workspace and checks if any of them are PostScript or TIFF.
* If any are, then the GraphicView is asked to load those in as objects.
* Very important: an NX_DURING handler is required around all the processing
* of this method since an uncaught raised error will cause this method not
* to return and thus hang the Workspace Manager for a while.
*/
- (int)iconReleasedAt:(double)x :(double)y ok:(int *)flag
{
volatile int foundOne = NO;
char *file, *tab, *extension;
int format;
id importpanel;
NXPoint pt;
NX_DURING
importpanel = [ImportPanel new];
format = [importpanel format];
[importpanel setFormat:IMPORT_COPY];
pt.x = x;
pt.y = y;
[windowId convertScreenToBase:&pt];
[drawingviewId convertPoint:&pt fromView:nil];
file = iconPathList;
while (file)
{
tab = strchr(file, '\t');
if (tab)
*tab = '\0';
extension = strrchr(file, '.');
if (extension)
{
if (!strcmp(extension, ".ps") ||
!strcmp(extension, ".eps") ||
!strcmp(extension, ".tiff"))
if ([drawingviewId importFile:file at:&pt])
foundOne = YES;
}
file = tab ? tab++ : NULL;
}
if (foundOne)
{
[NXApp activateSelf:YES];
[windowId makeKeyAndOrderFront:self];
}
[importpanel setFormat:format];
NX_HANDLER
NX_ENDHANDLER
*flag = foundOne;
return 0;
}
/* Validates whether a menu command makes sense now */
/*
* Validates whether a menu command that DrawDocument responds to
* is valid at the current time.
*/
- (BOOL)validateCommand:menuCell
{
SEL action = [menuCell action];
if (action == @selector(save:))
return ([drawingviewId isDirty] || !saved);
else if (action == @selector(saveTo:))
return ([drawingviewId isSelected]);
else if (action == @selector(revertToSaved:))
return ([drawingviewId isDirty] && saved);
return YES;
}
/* Cursor-setting method */
/*
* Resets the document's cursor rectangle to be the frame of the
* drawing view.
* Makes the drawing view the first responder if
* there isn't one or if no tool is selected.
*/
- resetResponder
{
id responder;
responder = [windowId firstResponder];
if (!responder || responder == windowId || [NXApp cursor] == NXArrow)
[windowId makeFirstResponder:drawingviewId];
return self;
}
@end